package com.log320.huffman;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

import javax.swing.JPanel;

/**
 * Implementation of the ActionListener class used when the compression is requested.
 * 
 */
public class CompressActionListener implements ActionListener{
	public HuffmanFrame m_frame;

	public CompressActionListener(HuffmanFrame myFrame){
		m_frame = myFrame;
	}
	@Override
	public void actionPerformed(ActionEvent e){
		//Lire le fichier en premier lieu
		compress();		
	}
	
	
/**
 * read the file from the input TextField, compress and write it to the
 * output TextField
 */
	private void compress(){
		Reader inputStream=null;
		long initTime = System.currentTimeMillis();
        try {
        	File file = new File(m_frame.getSourceFileText());
        	FileInputStream fis = new FileInputStream(file);
        	InputStreamReader isr = new InputStreamReader(fis,"ISO-8859-1");
            inputStream = new BufferedReader(isr);
            
        	// Performance :: O(m)		m = nb of characters in the file
            int c;
            int[] sequenceTable = new int[256];
            ArrayList<Integer> fileContent= new ArrayList<Integer>();
            while ((c = inputStream.read()) != -1) {
            		sequenceTable[c]++;
            		fileContent.add(c);
            }
            
            LinkedList<BinaryComponent> list = new LinkedList<BinaryComponent>();
            //transform array into an LinkedList of Node (removing characters with frequency of 0)
            // Performance :: O(256)
            for (int i = 0; i < 256; i++){
            	if (sequenceTable[i] !=0 ){
            		list.add(new BinaryLeaf(i, sequenceTable[i]));
            	}
            }
            // Sort list in increasing order of frequency
            // Performance :: O(n*log(n))  n = list.size() <=256
            Collections.sort(list);
            
            BinaryComponent root = buildBinaryTree(list);
                        
            //Instanciation of an arrayList of binary code for character 0-255 with null as a binary code
            ArrayList<BinaryCode> binaryCharList = new ArrayList<BinaryCode>(256);
            for (int i = 0; i < 256; i++){
            	binaryCharList.add(new BinaryCode(i));
            	}

            LinkedList<Boolean> tempBitList = new LinkedList<Boolean>();
            // we build the initial code book (initialise the binary code)
            list.getFirst().buildLeafArray(binaryCharList,tempBitList);
            
            // we sort by code length than by alphabetical order(for same codelength)
            Collections.sort(binaryCharList);
            
            // we replace the binary code by canonical generated code
            CannonicalConverter.toCannonical(binaryCharList);
            
            // We sort the list alphabetically
            Collections.sort(binaryCharList,new BinaryCodeAlphabeticalComparator());
                        
            BitOutputStream bos=null;
    		try {
    			BufferedOutputStream buffos = new BufferedOutputStream(new FileOutputStream(m_frame.getOutputFilePathText()));
    			bos = new BitOutputStream(buffos);
    	        BinaryCode codeValue;
    	        
    	        //Write header
    	        bos.write(fileContent.size());
    	        BinaryCode bin=null;
    	        for(int i =0;i<256;i++){
    	        	bos.writeByte(binaryCharList.get(i).size());
    	        }
                for(int i=0;i < fileContent.size();i++){
                	codeValue = binaryCharList.get(fileContent.get(i));
                	int j=0;
                	for(j=0;j < codeValue.size();j++){
                		bos.write(codeValue.get(j));
                	}
                }
    		} catch (FileNotFoundException e) {
    		}catch (IOException ioe){
    		}finally{
    			bos.close();
    		}

        }catch(FileNotFoundException ex){
        	
        }catch(IOException ioe){
        	
        }
        finally {
        	try{
        		if (inputStream != null) {
        			inputStream.close();
        		}
            }catch(FileNotFoundException ex){
            	
            }catch(IOException ioe){}
        }
        System.out.println(System.currentTimeMillis()-initTime);    
	}
        
        

	
	private BinaryComponent buildBinaryTree(LinkedList<BinaryComponent> list)
	{
		while(list.size()!= 1 ) // O(list.size())
		{
			boolean done = false;
			BinaryComposite comp = new BinaryComposite(list.removeFirst(), list.removeFirst());
			ListIterator iter = list.listIterator();
			while (iter.hasNext() && !done)
			{
				if (((BinaryComponent)iter.next()).frequency >= comp.frequency )
				{
					iter.previous();
					iter.add(comp);
					done = true;
				}
			}
			if(!done)
				list.addLast(comp);
		}
		return list.getFirst();
	}
}
